﻿//////////////////////////////////////////////////////////////////////////////
//
// Copyright © 1998-2024 Glodon Company Limited.
//
// Licensed under the MIT License
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the “Software”),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
//////////////////////////////////////////////////////////////////////////////
#include "GmLinearPatternCmd.h"
#include "GbmpUiPlatformCommandIds.h"
#include "GbmpUiPlatformCommandRegister.h"
#include "CmdStatusUtils.h"
#include "IUiDocument.h"
#include "ISelection.h"
#include "IDocument.h"
#include "IElement.h"
#include "IGraphicsNodeReference.h"
#include "IUserTransaction.h"
#include "IActionInputEventArgs.h"
#include "IElementTransformationComponent.h"
#include "INotificationDetailItem.h"
#include "INotificationManager.h"
#include "INotification.h"
#include "ILineEditDefinition.h"
#include "IRadioButtonGroupDefinition.h"
#include "LinearArrayUtils.h"
#include "UiDocumentViewUtils.h"
#include "IBehaviorConfig.h"

#include "IGenericElement.h"
#include "IDrawingViewport.h"
#include "UiCommonDialog.h"

#include "NotificationUtils.h"
#include "ActionDrawStraightElementCreator.h"
#include "EnableCompileWarning_The_LAST_IncludeInCpp.h"

using namespace gcmp;

#define ACTION_EDIT_ARRAY_COUNT         L"gmEditArrayCount"
#define ACTION_RADIO_MOVE_TO            L"gmRadioMoveTo"
#define RADIOBUTTON_LAST                L"gmLastRadioButton"
#define RADIOBUTTON_SECOND              L"gmSecondRadioButton"


REGISTER_GM_COMMAND(GmLinearPatternCmd)

GmLinearPatternCmd::GmLinearPatternCmd()
    : GbmpCommandBase(ID_CMD_GBMP_LINEAR_PATTERN)
    , m_iArrayCount(2)
    , m_bDistanceToLastPoint(false)
{
}

GmLinearPatternCmd::~GmLinearPatternCmd()
{
}

bool GmLinearPatternCmd::IsVisible() const
{
    CMD_ONLY_VISIBLE_IN_DOCUMENT_ENVIRONMENT
}

bool GmLinearPatternCmd::IsEnabled() const
{
    CMD_ONLY_ENABLE_IN_DOCUMENT_ENVIRONMENT
}

gcmp::OwnerPtr<IAction> GmLinearPatternCmd::ExecuteCommand(const gcmp::CommandParameters& cmdParams)
{
    const GraphicsNodeReferenceOwnerPtrSet& selectedElems = ISelection::Get()->GetGraphicsNodeReferences();
    if (selectedElems.empty())
    {
        std::vector<OwnerPtr<INotificationDetailItem>> detailItems;
        INotificationManager::Get()->AddNotification(TransferOwnership(INotification::CreateNotification(NotificationTypes::InfomationNotificationType, GBMP_TR(L"请先选中对象，再进行阵列操作。"), std::move(detailItems))));
        return nullptr;
    }

    IDocument* pDoc = UiDocumentViewUtils::GetCurrentDocument();
    DBG_WARN_AND_RETURN_NULLPTR_UNLESS(pDoc, L"获取pDoc失败",L"GDMPLab",L"2024-03-30");
    FOR_EACH(selectedItem, selectedElems)
    {
        IElement* pElement = pDoc->GetElement(selectedItem->GetElementId());
        if (pElement)
        {
            IGenericElement* pGenericElement = quick_cast<IGenericElement>(pElement);
            if (pGenericElement)
            {
                IDrawingViewport* pViewport = quick_cast<IDrawingViewport>(pGenericElement->GetExternalObject());
                if (pViewport)
                {
                    UiCommonDialog::ShowMessageBox(GBMP_TR(L"阵列"), GBMP_TR(L"视口不支持阵列！"), (int)UiCommonDialog::ButtonType::OK);
                    return nullptr;
                }
            }
        }
    }

    if (IBehaviorConfig::GetConfigRecordValueAsString(L"KeepPreEditValue") != L"true")
    {
        m_iArrayCount = 2;
    }
    m_bDistanceToLastPoint = false;

    // 配置Action
    ActionDrawStraightElementInput actionInput;
    actionInput.CommandId = GetCommandId();
    actionInput.LineFinishedCallback = actionInput.CreateOnLineFinishedCallback(&GmLinearPatternCmd::OnLineFinished, this);
    actionInput.InputDialogCallback = actionInput.CreateDefineInputDialogCallback(&GmLinearPatternCmd::DefineInputDialog, this);
    actionInput.InputEventCallback = actionInput.CreateOnActionInputEventCallback(&GmLinearPatternCmd::OnInputDialogValueChanged, this);

    return ActionDrawStraightElementCreator::Create(actionInput);
}

ElementId GmLinearPatternCmd::OnLineFinished(gcmp::IUiView* pCurrentView, const gcmp::Vector3d& startPt, const gcmp::Vector3d& endPt,
    const gcmp::ISnap* pStartSnap, const gcmp::ISnap* pEndSnap)
{
    if (m_iArrayCount < 2)
    {
        NotificationUtils::PostNotification(NotificationTypes::InfomationNotificationType, GBMP_TR(L"阵列个数小于2，不需要阵列"));
        return ElementId::InvalidID;
    }
    IDocument* pDoc = UiDocumentViewUtils::GetCurrentDocument();
    DBG_WARN_AND_RETURN_UNLESS(pDoc, ElementId::InvalidID, L"doc为空",L"GDMPLab",L"2024-03-30");

    const GraphicsNodeReferenceOwnerPtrSet& selectedElems = ISelection::Get()->GetGraphicsNodeReferences();

    std::set<ElementId> selectElementIds;
    FOR_EACH(item, selectedElems)
    {
        ElementId elementId = item->GetElementId();
        const IElement* pElement = pDoc->GetElement(elementId);
        if (!pElement)
        {
            continue;
        }

        if (const IGenericElement* pGenericElement = quick_cast<IGenericElement>(pElement))
        {
            const IDrawingViewport* pViewport = quick_cast<IDrawingViewport>(pGenericElement->GetExternalObject());
            if (pViewport)
            {
                // 视口不能进行阵列操作
                continue;
            }
        }

        if (const IElementTransformationComponent *pElementTransformationComponent = pElement->GetElementTransformationComponent())
        {
            if (const IElementCopyPasteComponent* pElementCopyPasteComponent = pElement->GetCopyPasteComponent())
            {
                std::wstring cannotArrayedExplanation;
                if (pElementTransformationComponent->CanBeMoved(&cannotArrayedExplanation) &&
                    pElementCopyPasteComponent->CanBeCopied(&cannotArrayedExplanation))
                {
                    selectElementIds.emplace(elementId);
                }
            }
        }
    }

    if (selectElementIds.empty())
    {
        UiCommonDialog::ShowMessageBox(GBMP_TR(L"阵列"), GBMP_TR(L"没有满足条件的阵列对象。"), static_cast<int>(UiCommonDialog::ButtonType::OK));
        return ElementId::InvalidID;
    }

    Vector3d arrayVector = GetArrayVector(startPt, endPt);
    OwnerPtr<IUserTransaction> ut = IUserTransaction::Create(pDoc, GBMP_TR(L"线性阵列"), true);
    DBG_WARN_AND_RETURN_UNLESS(ut != nullptr, ElementId::InvalidID, L"事务创建失败",L"GDMPLab",L"2024-03-30");

    std::set<std::pair<ElementId, std::wstring>> pErrorMessages;
    LinearArrayUtils::LinearArray(pDoc, selectElementIds, arrayVector, arrayVector.Length(), m_iArrayCount, nullptr, &pErrorMessages);
    if (!pErrorMessages.empty())
    {
        auto errorItem = pErrorMessages.begin();
        NotificationUtils::PostNotification(NotificationTypes::InfomationNotificationType, GBMP_TR(errorItem->second));
        ut->Rollback();
    }
    else
    {
        ut->Commit();
    }
    
    return ElementId::InvalidID;
}

void GmLinearPatternCmd::DefineInputDialog(DefineInputDialogParams& inputItems)
{
    // 显示数据输入界面。
    inputItems.title = GBMP_TR(L"输入阵列参数");

    OwnerPtr<ILineEditDefinition> opEditArrayCountLineEditDefinition = ILineEditDefinition::Create(ACTION_EDIT_ARRAY_COUNT, GBMP_TR(L"阵列个数"), LineEditInputType::InputInt);
    DBG_WARN_AND_RETURN_VOID_UNLESS(opEditArrayCountLineEditDefinition, L"opEditArrayCountLineEditDefinition为空",L"GDMPLab",L"2024-03-30");
    std::wstring sArrayCount = StringUtil::FormatWString(L"%d", m_iArrayCount);
    opEditArrayCountLineEditDefinition->SetText(sArrayCount);
    inputItems.inputItems.push_back(TransferOwnership(opEditArrayCountLineEditDefinition));

    OwnerPtr<IRadioButtonGroupDefinition> opRadioMoveToRadioButtonGroupDefinition = IRadioButtonGroupDefinition::Create(ACTION_RADIO_MOVE_TO, GBMP_TR(L"移动到："));
    DBG_WARN_AND_RETURN_VOID_UNLESS(opRadioMoveToRadioButtonGroupDefinition, L"opRadioMoveToRadioButtonGroupDefinition为空",L"GDMPLab",L"2024-03-30");
    opRadioMoveToRadioButtonGroupDefinition->AddRadioButton(RADIOBUTTON_SECOND, GBMP_TR(L"第二个"), GBMP_TR(L"第二个"));
    opRadioMoveToRadioButtonGroupDefinition->AddRadioButton(RADIOBUTTON_LAST, GBMP_TR(L"最后一个"), GBMP_TR(L"最后一个"));
    inputItems.inputItems.push_back(TransferOwnership(opRadioMoveToRadioButtonGroupDefinition));
}

const std::wstring GmLinearPatternCmd::DefinePromptMsg(DrawLineStatus drawLineStatus)
{
    switch (drawLineStatus)
    {
    case DrawLineStatus::Getting_StartPoint:
        return GBMP_TR(L"用选择的物体创建阵列 - 请输入起始点");
    case DrawLineStatus::Getting_EndPoint:
        return GBMP_TR(L"用选择的物体创建阵列 - 请输入终止点");
    default:
        return L"";
    }
}

void GmLinearPatternCmd::OnInputDialogValueChanged(gcmp::IActionInputEventArgs* pArgs)
{
    DBG_WARN_AND_RETURN_VOID_UNLESS(pArgs, L"pArgs为空",L"GDMPLab",L"2024-03-30");
    const std::pair<std::wstring, gcmp::Any>& msg = pArgs->GetInputMessage();
    const std::wstring eventID = msg.first;
    gcmp::Any inputValue = msg.second;
    if (eventID == ACTION_EDIT_ARRAY_COUNT)
    {
        m_iArrayCount = inputValue.cast<int>();
    }
    else if (eventID == ACTION_RADIO_MOVE_TO)
    {
        m_bDistanceToLastPoint = inputValue.cast<std::wstring>() != RADIOBUTTON_SECOND;
    }
}

Vector3d GmLinearPatternCmd::GetArrayVector(const Vector3d& startPt, const Vector3d& endPt) const
{
    Vector3d distance = endPt - startPt;
    if (m_bDistanceToLastPoint)
    {
        distance /= (m_iArrayCount-1);
    }
    return distance;
}
